在 Workbox 详解篇:预缓存中,我们简单介绍了 workbox-webpack-plugin,本章我们将深入介绍 workbox-webpack-plugin 的底层依赖 workbox-build,通过该模块,我们可以:

  • 根据配置,直接生成 Service Worker 脚本;
  • 根据配置,生成预缓存列表,并将其附加到已有的 Service Worker 脚本中。
  • 接下来,我们将一起探讨 workbox-build 的使用细节。

# generateSW

const { generateSW } = require('workbox-build');

generateSW({
  //... 其他配置
  swDest: 'public/sw.js',
  globDirectory: '.'
}).then(({ count, size, warnings }) => {
  //...doSomething
});

示例中,我们通过 workbox-build 中的 generateSW 方法来生成 Service Worker 脚本,该方法的返回值为 Promise<GenerateSWResult>,其中 GenerateSWResult 为含有以下属性的对象:

  • count:预缓存列表中的文件个数。
  • size:预缓存文件的总尺寸大小,单位为 byte
  • warnings:构建过程中所产生的警告信息,类型为字符串数组。

该方法的配置属性为:

  • swDest:将要生成 Service Worker 脚本的路径及文件名,如果值为相对路径,那么:
    • 如果运行在 node 环境中,路径相对于当前的工作目录。
    • 如果运行在 webpack 环境中,路径相对于 output 配置中的 path 属性。
  • importWorkboxFrom:Workbox入口文件 workbox-sw.js 的加载路径,可选值为:
    • cdn:默认值,将从 Google Cloud Storage 中加载 workbox-sw.js,由于国内网络问题,一般不使用该选项。
    • local:如使用该选项,在构建过程中会将 Workbox 模块代码拷贝到构建目录中,并从该目录中加载workbox-sw.js
    • disabled:如使用该选项,Service Worker 脚本将不会包含加载 workbox-sw.js的代码。
  • skipWaiting:是否在 install 事件中调用 skipWaiting 方法(Boolean 类型,默认值为 false),如果该属性的值为 false,Service Worker 脚本将包含以下代码:
self.addEventListener('message', (event) => {
  if (event.data && event.data.type === 'SKIP_WAITING') {
    self.skipWaiting();
  }
});
  • clientsClaim:是否在 activate事件中调用 clients.claim 方法(Boolean 类型,默认值为 false)。
  • runtimeCaching:运行时缓存配置,类型为对象数组(默认值为 []),其中每一项包含以下属性:
    • urlPatterns:请求匹配规则,类型为字符串、正则表达式或函数,其中:
    • handler:请求处理,类型为字符串或函数,其中:
      • 值为字符串时,其值必须为 CacheFirstCacheOnlyNetworkFirstNetworkOnlyStaleWhileRevalidate
      • 值为函数时,用法等同于 Workbox 详解篇:路由设置中 workbox.routing.registerRoute方法的 handler 参数
  • method:请求方法,值为 GETHEADPOSTPATCHPUTDELETE,默认值为 GET
  • optionshandler 为字符串时的配置参数,主要属性有:
    • cacheName:缓存名称,默认值为 Workbox 配置中的运行时缓存名。
    • fetchOptions:网络请求配置信息,结构与函数 fetch 中的 init 参数一致(在 CacheOnly 中,该属性将会被忽略)。
    • matchOptionsCacheQueryOptions 对象(在 NetworkOnly 中,该属性将会被忽略)。
    • networkTimeoutSeconds:如果对该属性进行了赋值,那么网络会在指定的时间内没有响应时使用本地缓存来进行响应(该属性仅在 NetworkFirst 中有效)。
    • expiration:插件 workbox.expiration.Plugin 配置信息,详情参见:Workbox 详解篇:请求策略 & 缓存置换策略相关内容。
    • backgroundSync:插件 workbox.backgroundSync.Plugin配置信息,详情参见:Workbox 详解篇:后台同步相关内容。
    • cacheableResponse:插件 workbox.cacheableResponse.Plugin 配置信息,详情参见:Workbox 详解篇:可缓存对象相关内容。
    • broadcastUpdate:插件 workbox.broadcastUpdate.Plugin 配置信息,详情参见:Workbox 详解篇:缓存更新广播相关内容。
    • `plugins:自定义插件列表,自定义插件详情参见:Workbox 详解篇:插件相关内容。
  • navigateFallback:使用该属性的值作为键值,从预缓存中获取指定资源来响应所有的导航请求。
  • navigateFallbackBlacklist:正则表达式数组,作用等同于 workbox.routing.registerNavigationRouteblacklist 属性。
  • navigateFallbackWhitelist:正则表达式数组,作用等同于 workbox.routing.registerNavigationRoutewhitelist 属性。
  • importScripts:需要加载的脚本列表。
  • ignoreURLParametersMatching:预缓存配置,用于忽略请求中符合指定规则的查询参数,详情参见:Workbox 详解篇:预缓存相关内容。
  • directoryIndex:预缓存配置,默认值为 index.html,即以预缓存 /index.html 来响应 / 的请求。
  • cacheId:用于设置缓存名配置中的 prefix 属性。
  • offlineGoogleAnalytics:是否启用离线 Google Analytics(Boolean 类型,默认值为 false)。
  • cleanupOutdatedCaches:是否添加 workbox.precaching.cleanupOutdatedCaches() 调用来清理过期的预缓存项目(Boolean 类型,默认值为 false)。
  • navigationPreload:是否开启导航预加载(Boolean 类型,默认值为 false),当值为 true 时,需设置 runtimeCaching 对导航请求进行处理,且必须消费 event.preloadResponse
  • globPatterns:能够被加入到预缓存列表的文件匹配模式,类型为字符串模式数组,默认值
    • workbox-buildworkbox-cli 下为 ['**/*.{js,css,html}']
    • workbox-webpack-plugin 下为 []
  • globDirectory:文件遍历的起始目录,如果值为相对路径,则相对于当前工作目录,如设置了该属性,除非设置了 templatedURLs 属性,否则需同时设置 globPatterns 属性。
  • globFollow:文件匹配时,是否遵循符号链接规则(Boolean 类型,默认值为 true)。
  • globIgnores:文件匹配时,需要排除的文件匹配规则列表,默认值为 ['node_modules/**/*']
  • globStrict:文件匹配时,如果目录读取的过程中出现异常,如果值为 true 将抛出异常并终止构建进程,否则将忽略该目录,默认值为 true。
  • templatedURLs:默认情况下,加入到预缓存列表中每一项 revision 的值为文件的 hash 值,我们可通过该属性改变此默认行为。类型为对象,其中,每一项的键为文件的网络路径,值为:
    • 为字符串时,直接将该字符串的 hash 值作为预缓存文件的 revision 值。
    • 为字符串数组时,将被当作 globPatterns 进行文件匹配,将匹配到所有文件的总 hash 值作为预缓存文件的 revision 值。
  • maximumFileSizeToCacheInBytes:当文件尺寸大于指定大小时,将不会将其加入到预缓存列表中(单位为 byte,默认值为 2097152)。
  • dontCacheBustURLsMatching:类型为正则表达式,默认为空,当文件名符合指定规则时,在将其加入预缓存列表中将不会为该文件设置 revision 值。
  • modifyURLPrefix:修改预缓存列表中每一项 url 值的前缀,类型为对象,比如:
self.__precacheManifest = [
  {
    "url": "/dist/main.js",
    "revision": "0d12a38a0f7f730d93c6ff98082c6d99"
  }
].concat(self.__precacheManifest || []);

如果 modifyURLPrefix 配置如下:

modifyURLPrefix: {
  '/dist': ''
}

那么上述预缓存列表将被替换成:

self.__precacheManifest = [
  {
    "url": "/main.js",
    "revision": "0d12a38a0f7f730d93c6ff98082c6d99"
  }
].concat(self.__precacheManifest || []);
  • manifestTransforms:通过该属性,可以对预缓存列表进行自定义转换,类型为函数数组,函数签名为 (Array<ManifestEntry>) => ManifestTransformResult
    • ManifestEntry 为含有以下属性的对象:
      • url:文件地址,类型为字符串。
      • revision:文件 hash 值,类型为字符串。
    • ManifestTransformResult 为含有以下属性的对象:
      • manifest:文件列表,类型为 ManifestEntry 数组。
      • warnings:警告信息列表,类型为字符串数组。

如果同时设置了 dontCacheBustURLsMatchingmodifyURLPrefix 属性,那么该属性将在它们两个之后执行。

除了通过 generateSW 方法快速生成 Service Worker 脚本外,我们也可通过 generateSWSring 方法来生成 Service Worker 脚本的字符串,使用方法如下:

const { generateSWString } = require('workbox-build');

generateSWString({
  //... 其他配置
  globDirectory: '.',
}).then(({ swString, warnings }) => {
  //...doSomething
});

该方法的返回值为 Promise<GenerateSWStringResult>,其中 GenerateSWStringResult 为含有以下属性的对象:

  • swString:生成的脚本字符串。
  • warnings:构建过程中所产生的警告信息,类型为字符串数组。

该方法的配置属性除了没有 swDest 外,其他的与 generateSW 完全相同,此处不再重述。

# injectManifest

当需要添加较为复杂(比如:推送通知)的逻辑时,generateSW 或 generateSWString 方法已不能满足我们的需求,此时,可使用 injectManifest 方法来帮助生成预缓存列表,并将其与我们自己实现的 Service Worker 脚本进行合并,使用方法如下:

const { injectManifest } = require('workbox-build');

injectManifest({
  //... 其他配置
  swSrc: 'src/sw.js',
  swDest: 'public/sw.js',
  globDirectory: '.'
}).then(({ count, size, warnings }) => {
  //...doSomething
});

该方法的返回值等同于 generateSW 方法的返回值,此处不再重述。其配置属性主要有:

  • swSrc:包含自定义逻辑的 Service Worker 脚本的路径及文件名,如值为相对路径,则相对于当前工作目录,并且需要注意的是:
    • 在 node 环境中,脚本中需要包含以下代码:workbox.precaching.precacheAndRoute([]);
    • 在 webpack 环境中,脚本中需要包含以下代码:workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
  • swDest、globDirectory、globFollow、globIgnores、globPatterns、globStrict、templatedURLs、maximumFileSizeToCacheInBytes、dontCacheBustURLsMatching、modifyURLPrefix 及 manifestTransforms 等同于 generateSW 方法的相关属性,此处不再重述

同 generateSWString 一样,我们亦可通过 getManifest 方法来获取预缓存列表,使用方法如下:

const { getManifest } = require('workbox-build');

getManifest({
  //... 其他配置
  globDirectory: '.'
}).then(({ manifestEntries, count, size, warnings }) => {
  //...doSomething
});

该方法的返回值为 Promise<GetManifestResult>,其中 GetManifestResult 为含有以下属性的对象:

  • manifestEntries:预缓存资源列表,类型为 Array<ManifestEntry>,ManifestEntry 已在上文中作过说明,此处不再重述。
  • count:预缓存列表中的文件个数。
  • size:预缓存文件的总尺寸大小,单位为 byte。
  • warnings:构建过程中所产生的警告信息,类型为字符串数组。 该方法的配置属性除了没有 swSrc、swDest 外,其他的与 injectManifest 完全相同,此处不再重述。

# workbox-webpack-plugin

虽然 workbox-webpack-plugin 底层使用了 workbox-build,但在使用的过程中,依旧需要注意以下两点:

  • 如果某一 chunk 中包含 workbox-sw 模块的代码,那么亦可将该 chunk 的名称作为 importWorkboxFrom 属性的值。
  • 一般情况下,无需配置 globPatternsglobDirectoryglobFollowglobIgnoresglobStricttemplatedURLsmaximumFileSizeToCacheInBytesdontCacheBustURLsMatchingmodifyURLPrefix 属性,这是因为 workbox-webpack-plugin 会自动将webpack 打包编译的资源加入到预缓存列表中。这些属性仅用于将不能被 webpack 识别的资源添加到 预缓存列表 中,且 maximumFileSizeToCacheInBytesdontCacheBustURLsMatchingmodifyURLPrefix 对 webpack 打包编译的资源无效。

相对于 workbox-buildworkbox-webpack-plugin中的 GenerateSW 及 InjectManifest 新增了以下配置选项:

  • chunks:默认情况下,插件会将所有 chunk 下的资源加入到预缓存列表中,如果只想将指定 chunk 下的资源加入到预缓存列表中,可通过配置该选项实现(类型为字符串数组,每一项为 chunk 名称,默认值为 [])。
  • excludeChunks:与 chunks 的作用相反,如果配置了该选项,那么设置中指定的 chunk 下的资源将不会被加入到预缓存列表中(类型为字符串数组,每一项为 chunk 名称,默认值为 [])。
  • include:如果配置了该选项,插件便会只将满足指定规则的资源加入到预缓存列表中(类型为字符串或正则表达式数组),该选项在 chunksexcludeChunks 之后执行。
  • exclude:与 include 的作用相反,插件只将不满足指定规则的资源加入到预缓存列表中(类型为字符串或正则表达式数组,默认值为 [/\.map$/, /^manifest.*\.js$/]),该选项在 chunksexcludeChunks 之后执行。
  • importsDirectory:默认情况下,插件会将自动生成的包含预缓存列表信息的脚本文件、Workbox 模块代码(importWorkboxFrom 的值为 local 时才会拷贝)放置到 webpack output 配置中 path 属性所指定的顶层目录下,如果设置了该选项,插件会在顶层目录下创建指定目录,并将前面所述文件放置到该目录下。
  • precacheManifestFilename:插件会自动生成包含预缓存列表信息的脚本文件,该文件的默认名称为precache-manifest.[manifestHash].js,可通过该选项修改其默认名称,但名称中必须包含 [manifestHash]

# 总结

本章我们首先对 workbox-build 进行了详细介绍,然后讨论了 workbox-webpack-plugin 与 workbox-build 的差异。通过这些模块,我们既可以直接生成 Service Worker 脚本来满足简单需求,也可通过生成预缓存列表并与现有 Service Worker 脚本合并的方式来满足复杂需求。至此我们已完成了 Workbox 系列整个核心部分的学习,相信到此我们已经掌握了:

  • 如何使用 workbox.precaching.precacheAndRouteworkbox.precaching.PrecacheController 进行预缓存处理。
  • 如何使用 workbox.routing.registerRouteworkbox.routing.DefaultRouter 进行运行时路由设置。
  • 如何使用 workbox.strategies.*workbox.expiration.Plugin来处理请求策略及缓存置换问题。
  • 如何在 Workbox 处理导航预加载及后台同步问题。
  • Workbox 中可缓存对象、缓存更新广播及插件的使用。
  • 如何通过 Workbox Window 对象来轻松管理 Service Worker 的注册、更新及通信。
  • 如何使用 workbox-build 及 workbox-webpack-plugin 与现代 Web 构建工具结合来轻松管理 Service Worker 脚本。
阅读全文